package bma.common.langutil.core;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;

/**
 * 数据的类型
 * 
 * @author 关中
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 */
public class MType {

	public static final MType[] EMPTY = new MType[0];

	public static final MType OBJECT = new MType(Object.class, Object.class);

	public static final MType[] OBJECT_ARRAY = { OBJECT };

	public static final MType STRING = new MType(String.class, String.class);

	/**
	 * 对应的类
	 */
	private Class coreClass;

	/**
	 * 参数的类
	 */
	private Type type;

	/**
	 * 内部类
	 */
	private MType[] innerType;

	public MType() {
		super();
	}

	public MType(Type type) {
		super();
		this.coreClass = getClass(type);
		this.type = type;
	}

	public MType(Class coreClass, Type type) {
		super();
		this.coreClass = coreClass;
		this.type = type;
	}

	public MType(MType type2) {
		super();
		this.type = type2.getType();
		this.coreClass = type2.getCoreClass();
		this.innerType = type2.innerType;
	}

	public static MType create(Object obj) {
		if (obj == null)
			return null;
		return createByClass(obj.getClass());
	}

	public static MType createByClass(Class cls) {
		return new MType(cls, cls);
	}

	public Class getCoreClass() {
		return coreClass;
	}

	public void setCoreClass(Class coreClass) {
		this.coreClass = coreClass;
	}

	public Type getType() {
		return type;
	}

	public void setType(Type type) {
		this.type = type;
	}

	public String getTypeName() {
		if (this.type == null)
			return "<null>";
		return this.type.toString();
	}

	public MType[] getInnerType() {
		return innerType;
	}

	public void setInnerType(MType[] innerType) {
		this.innerType = innerType;
	}
	
	@Override
	public String toString() {
		StringBuffer buf = new StringBuffer(100);
		buf.append("MT[");
		buf.append(type);
		if (type != null) {
			if (!(type instanceof Class)) {
				buf.append(",");
				buf.append(coreClass);
			}
		}
		buf.append("]");
		return buf.toString();
	}

	public MType getInnerType(int idx, MType def) {
		MType t = getInnerType(idx);
		if (t == null)
			return def;
		return t;
	}

	public MType getInnerType(int idx) {
		MType[] it = this.innerType;
		if (it == null) {
			it = buildInnerType();
			this.innerType = it;
		}
		if (idx < it.length)
			return it[idx];
		return null;
	}

	public MType[] sureInnerType() {
		if (this.innerType == null) {
			this.innerType = buildInnerType();
		}
		return this.innerType;
	}

	protected Class getClass(Type type) {
		if (type instanceof Class) {
			return (Class) type;
		} else if (type instanceof GenericArrayType) {
			return Array.class;
		} else if (type instanceof ParameterizedType) {
			ParameterizedType pcls = (ParameterizedType) type;
			Type c = pcls.getRawType();
			if (c instanceof Class) {
				return (Class) c;
			}
			return getClass(c);
		} else if (type instanceof WildcardType) {
			WildcardType wc = (WildcardType) type;
			Type[] ts = wc.getUpperBounds();
			if (ts == null || ts.length == 0) {
				return Object.class;
			} else {
				if (ts.length > 1) {
					throw new IllegalArgumentException("unknow type [" + type
							+ "]");
				}
				return getClass(ts[0]);
			}
		} else if (type instanceof TypeVariable) {
			TypeVariable tc = (TypeVariable) type;
			Type[] ts = tc.getBounds();
			if (ts == null || ts.length == 0) {
				return Object.class;
			} else {
				if (ts.length > 1) {
					throw new IllegalArgumentException("unknow type [" + type
							+ "]");
				}
				return getClass(ts[0]);
			}
		} else {
			throw new IllegalArgumentException("unknow type [" + type + "]");
		}
	}

	protected MType[] buildInnerType() {
		if (type == null)
			return EMPTY;
		if (type instanceof Class) {
			Class c = (Class) type;
			if (c.isArray()) {
				MType[] r = new MType[1];
				r[0] = new MType(c.getComponentType(), c.getComponentType());
				return r;
			}
			return EMPTY;
		} else if (type instanceof GenericArrayType) {
			GenericArrayType acls = (GenericArrayType) type;
			Class aclazz = coreClass == null ? null : coreClass
					.getComponentType();
			Type atype = acls.getGenericComponentType();

			MType[] r = new MType[1];
			r[0] = new MType(aclazz, atype);
			return r;

		} else if (type instanceof ParameterizedType) {
			ParameterizedType pcls = (ParameterizedType) type;
			Type[] ts = pcls.getActualTypeArguments();
			if (ts.length == 0)
				return EMPTY;

			MType[] r = new MType[ts.length];
			for (int i = 0; i < ts.length; i++) {
				Type t = ts[i];
				r[i] = new MType(getClass(t), t);
			}
			return r;
		} else if (type instanceof WildcardType) {
			return EMPTY;
		} else if (type instanceof TypeVariable) {
			return EMPTY;
			/*
			 * TypeVariable tc = (TypeVariable) type; Type[] ts =
			 * tc.getBounds(); if (ts == null || ts.length == 0) { return
			 * MType.OBJECT_ARRAY; } else { MType[] r = new MType[ts.length];
			 * for (int i = 0; i < ts.length; i++) { Type t = ts[i]; r[i] = new
			 * MType(t, getClass(t)); } return r; }
			 */
		} else {
			throw new IllegalArgumentException("unknow type [" + type + "]");
		}
	}
}
